﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;

namespace lights
{

    //增加fp32Table结构，如果需要查表的操作，就需要传入
    //burst 环境比较严格
    public struct fp32Table
    {
        public NativeArray<int> anglexztable;
    }
    public struct fp32v3
    {
        public fp32 x;
        public fp32 y;
        public fp32 z;
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public fp32v3(fp32 x, fp32 y, fp32 z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        #region Compare方法
        public override bool Equals(object other)
        {
            if (other is fp32v3 v)
            {
                return x == v.x && y == v.y && z == v.z;
            }
            else
            {
                return false;
            }
        }
        public override int GetHashCode()
        {
            return ToStringFix().GetHashCode();
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool operator ==(fp32v3 lhs, fp32v3 rhs) { return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z; }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool operator !=(fp32v3 lhs, fp32v3 rhs) { return lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z; }
        #endregion
        public override string ToString()
        {
            return string.Format("{0},{1},{2}", x, y, z);
        }
        public string ToStringFix()
        {
            return string.Format("{0},{1},{2}", x.ToStringFix(), y.ToStringFix(), z.ToStringFix());
        }
        public float3 ToFloat3()
        {
            return new float3(x.ToFloat(), y.ToFloat(), z.ToFloat());
        }
        public int3 ToInt3()
        {
            return new int3(x.ToInt(), y.ToInt(), z.ToInt());
        }
        public static readonly fp32v3 zero = new fp32v3(new fp32(0), new fp32(0), new fp32(0));

        public static fp32v3 FromFloat3(float3 v)
        {
            return new fp32v3(fp32.FromFloat(v.x), fp32.FromFloat(v.y), fp32.FromFloat(v.z));
        }
        public static fp32v3 FromFloat3(float x, float y, float z)
        {
            return new fp32v3(fp32.FromFloat(x), fp32.FromFloat(y), fp32.FromFloat(z));
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp32v3 operator +(fp32v3 lhs, fp32v3 rhs) { return new fp32v3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z); }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp32v3 operator -(fp32v3 lhs, fp32v3 rhs) { return new fp32v3(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z); }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp32v3 operator /(fp32v3 lhs, fp32 value) { return new fp32v3(lhs.x / value, lhs.y / value, lhs.z / value); }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp32v3 operator *(fp32v3 lhs, fp32 value) { return new fp32v3(lhs.x * value, lhs.y * value, lhs.z * value); }

        public static fp32v3 elem_Mul(fp32v3 a, fp32v3 b)
        {
            return new fp32v3(a.x * b.x, a.y * b.y, a.z * b.z);
        }
        public static fp32v3 elem_Div(fp32v3 a, fp32v3 b)
        {
            return new fp32v3(a.x / b.x, a.y / b.y, a.z / b.z);
        }
        public static fp32 DotOld(fp32v3 a, fp32v3 b)
        {
            return a.x * b.x + a.y * b.y + a.z * b.z;
        }

        public static fp32 Dot(ref fp32v3 a, ref fp32v3 b)
        {
            return a.x * b.x + a.y * b.y + a.z * b.z;
        }

        public static fp32v3 Dot(fp32 lhs, fp32v3 rhs)
        {
            return new fp32v3(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z);
        }

        public static fp32v3 Cross(fp32v3 a, fp32v3 b)
        {
            return new fp32v3(a.y * b.z - b.y * a.z, a.z * b.x - b.z * a.x, a.x * b.y - b.x * a.y);
        }
        //（x1,y1,z1）×（x2,y2,z2）=(y1z2-y2z1,z1x2-z2x1,x1y2-x2y1）
        public static fp32 LengthOld(fp32v3 fp3)
        {
            fp32 nearLen = NearLenOld(fp3);
            if (nearLen > fp32.FromInt(100))
            {
                fp32v3 near = fp3 / nearLen;
                fp32 len = fp32.Sqrt(DotOld(near, near));
                return len * nearLen;
            }
            else
            {
                return fp32.Sqrt(DotOld(fp3, fp3));
            }
        }
        public static fp32 Length(ref fp32v3 fp3)
        {
            fp32 nearLen = NearLen(ref fp3);
            if (nearLen > fp32.onehundred)
            {
                fp32v3 near = fp3 / nearLen;
                fp32 len = fp32.SqrtFast2(Dot(ref near, ref near));
                return len * nearLen;
            }
            else
            {
                return fp32.SqrtFast2(Dot(ref fp3, ref fp3));
            }
        }
        public static fp32 DistanceOld(fp32v3 a, fp32v3 b)
        {
            return LengthOld(a - b);
        }
        public static fp32 Distance(ref fp32v3 a, ref fp32v3 b)
        {
            fp32v3 dist;
            dist.x.value = a.x.value - b.x.value;
            dist.y.value = a.y.value - b.y.value;
            dist.z.value = a.z.value - b.z.value;
            return Length(ref dist);
        }
        //计算夹角
        public static fp32 CalcAngleOld(fp32v3 dir1, fp32v3 dir2)
        {
            var dot = fp32v3.DotOld(NormalizeOld(dir1), NormalizeOld(dir2));
            if (dot.value < -fp32.Scalar)
                dot.value = -fp32.Scalar;
            if (dot.value > fp32.Scalar)
                dot.value = fp32.Scalar;
            return fp32.Acos(dot);
        }

        //计算xz平面的 向量角度值，以0,0,1 为正

        public static void Init(ref fp32Table table)
        {
            if (table.anglexztable.IsCreated)
                return;

            table.anglexztable = new NativeArray<int>(65536 * 8, Allocator.Persistent);
            fp32v3 right = new fp32v3(fp32.zero, fp32.zero, fp32.one);
            for (int karea = 0; karea <= 4; karea += 4)
            {
                for (var xarea = 0; xarea <= 2; xarea += 2)
                {
                    for (var zarea = 0; zarea <= 1; zarea += 1)
                    {

                        for (var k = 0; k < 65536; k++)
                        {
                            var key = (karea + xarea + zarea) * 65536 + k;

                            int xsigh = xarea == 0 ? -1 : 1;
                            int zsign = zarea == 0 ? -1 : 1;

                            if (karea == 0) //dir1.z < dir1.x
                            {
                                fp32v3 dir = new fp32v3(fp32.FromFloat(xsigh), fp32.zero, new fp32(zsign * k));
                                table.anglexztable[key] = fp32v3.CalcAngleOld(dir, right).value;
                            }
                            else
                            {
                                fp32v3 dir = new fp32v3(new fp32(xsigh * k), fp32.zero, fp32.FromFloat(zsign));
                                if (key >= table.anglexztable.Length)
                                {
                                    Debug.LogWarning("key=" + key + " " + karea + "," + xarea + "," + zarea + ",k=" + k);
                                }
                                table.anglexztable[key] = fp32v3.CalcAngleOld(dir, right).value;

                            }

                        }
                    }
                }
            }
        }
        public static fp32 Dir2AngleXZ(ref fp32Table table, ref fp32v3 dir1)
        {
            if (dir1.x == fp32.zero)
            {
                if (dir1.z < fp32.zero)
                {
                    return fp32.PI;
                }
                else
                    return fp32.zero;
            }
            if (dir1.z == fp32.zero)
            {
                if (dir1.x < fp32.zero)
                {
                    return fp32.PIHalf;
                }
                else
                {
                    return fp32.PI3_4;
                }
            }
            var ax = fp32.Abs(dir1.x);
            var az = fp32.Abs(dir1.z);
            if (az < ax)//四个象限，八种情况 
            {
                fp32 k = az / ax;
                int areax = dir1.x > fp32.zero ? 2 : 0;
                int areaz = dir1.z > fp32.zero ? 1 : 0;
                int areak = 0;
                var key = (areak + areax + areaz) * 65536 + k.value % 65536;
                return new fp32(table.anglexztable[key]);
            }
            else
            {
                fp32 k = ax / az;
                int areax = dir1.x > fp32.zero ? 2 : 0;
                int areaz = dir1.z > fp32.zero ? 1 : 0;
                int areak = 4;
                var key = (areak + areax + areaz) * 65536 + k.value % 65536;
                return new fp32(table.anglexztable[key]);
            }
        }
        public static fp32 CalcAngle(ref fp32Table table, ref fp32v3 dir1, ref fp32v3 dir2)
        {
            fp32 a1 = Dir2AngleXZ(ref table, ref dir1);
            fp32 a2 = Dir2AngleXZ(ref table, ref dir2);

            var a = fp32.Abs(a2 - a1);
            if (a > fp32.PI)
                a = fp32.PI2 - a;
            //% fp32.PI2;
            return a;

        }
        public static fp32v3 Lerp(fp32v3 a, fp32v3 b, fp32 weight)
        {
            return new fp32v3(fp32.Lerp(a.x, b.x, weight), fp32.Lerp(a.y, b.y, weight), fp32.Lerp(a.z, b.z, weight));
        }
        public static fp32v3 NormalizeOld(fp32v3 fp3)
        {
            var len = LengthOld(fp3);
            if (fp32.zero == len)
            {
                return zero;
            }
            return fp3 / len;
        }

        public static fp32v3 Normalize(ref fp32v3 fp3)
        {

            fp32 nearLen = NearLen(ref fp3);
            if (nearLen > fp32.onehundred)
            {
                fp32v3 near = fp3 / nearLen;
                fp32 len = fp32.SqrtFast2(Dot(ref near, ref near));
                return near / len;
            }
            else
            {
                fp32 len = fp32.SqrtFast2(Dot(ref fp3, ref fp3));
                return fp3 / len;
            }
        }

        public static fp32v3 Floor(fp32v3 x)
        {
            return new fp32v3(fp32.Floor(x.x), fp32.Floor(x.y), fp32.Floor(x.z));
        }

        public static fp32 NearLenOld(fp32v3 v)
        {
            fp32 absX = fp32.Abs(v.x);
            fp32 absY = fp32.Abs(v.y);
            fp32 absZ = fp32.Abs(v.z);
            return fp32.Max(fp32.Max(absX, absY), absZ);
        }
        public static fp32 NearLen(ref fp32v3 v)
        {

            int vx = v.x.value;
            if (vx < 0) vx = 0;
            int vy = v.y.value;
            if (vy < 0) vy = 0;
            int vz = v.z.value;
            if (vz < 0) vz = 0;
            if (vx < vy)
            {
                return new fp32(vy > vz ? vy : vz);
            }
            return new fp32(vx > vz ? vx : vz);
        }
    }

}